#ifndef _TagWin_h_
#define _TagWin_h_

// My creation:  Allows easier cloning of window pages
#undef WINNAME
#define WINNAME TagWin

/*
  About window with some features: Remembers position, modeless so can leave open, copyable information
*/
#include "PowerWindow.h" // Contains TopWindow
#include "PowerArray.h" // Allow us to traverse the playlist and cherrypick songs to look at
#include "TagHandler.h" // All our tools for working with tags
#include "MainWinIDs.h" // We have to reference the columns on the main list to pull into a vector.

// U++ connection
#include <CtrlLib/CtrlLib.h>
#include "DbHandler.h"

using namespace Upp;

class MainWin;  // We reference functions in the main set.

class TagWin : public WithTagLayout<PowerWindow> {

public:
	StaticMutex mutex;
	PowerArray *selectedList;
	int listIndex;
	TagHandler tagHandler;
	DbSession db;
	typedef TagWin CLASSNAME;
	DbHandler *dbHandler;

	//============================================================================================================================
	TagWin() {
		RTIMING(String().Cat() << "Creating " << QUOTEME(WINNAME));
		// Following must be in constructor or will abort on 2nd open
		CtrlLayout(*this);
		dbHandler = NULL;
		cancelOp.Cancel() <<= Rejector(IDCANCEL);
		okOp.Ok() <<= Acceptor(IDOK);
		writeToDb.WhenPush = THISBACK(UpdateTagsInDb);
		selectedList = NULL; listIndex = -1;
	}

	//============================================================================================================================
	~TagWin() {
		RTIMING(String().Cat() << "Destroying " QUOTEME(WINNAME));
	}

	// SPEED: Note: Moved from constructor to open and increased  load time by about 50-100 ms (hard to measure)
	//============================================================================================================================
	void Open() {

		okOp.Hide(); // Ok button doesn't make sense if we are strolling through
		
		cancelOp.SetData("Done"); // Instead of cancel
		// Note that controls are text so that they can be user copied and selected
		PowerWindow::Open("tagwin");
	}

	//============================================================================================================================
	void Close() {
		// Serialize, mostly position information
		PowerWindow::Close("tagwin");  // Make sure to call PowerWindow close and not directly call TopWindow or else configuration is not saved possibly until application closes
	}

	//============================================================================================================================
	// Links back to calling win so that I can keep the decolumnization of the grid into a vector in there.  Hmmm.
	//============================================================================================================================
	void Open(PowerArray *lselectedList) {
		selectedList = lselectedList;
		if (selectedList)
			listIndex = selectedList->GetCursor(); // Get the current position of user in the grid so we can traverse from this screen without going back.
		
		nextFile.SetImage(CtrlsImg::DA());
		prevFile.SetImage(CtrlsImg::UA());
		nextFile.WhenPush = THISBACK(GetNextFileForTagging);
		prevFile.WhenPush = THISBACK(GetPrevFileForTagging);
		FetchFromGridAndPopulate();
		Open(); // Look, dammit, there's no easier way to do this.
	}
	
	//============================================================================================================================
	virtual void Xmlize(XmlIO xml) {
		
	}
	
	//============================================================================================================================
	// Set up Field on screen with the value from the tag vector.  If no value, put an empty value there.
	//============================================================================================================================
	void SetupField(DocEdit &fld, TagVector &tags, int tagid) {
		fld.SetData(tags.Get(tagid, String::GetVoid()));
		fld.ClearModify(); // The "SetData" command sets the Modified bit, so clear it so we can trap user changes
	}

	//============================================================================================================================
	void SetupField(EditField &fld, TagVector &tags, int tagid) {
		fld.NullText("Missing");
		WString prepWStr = tags.Get(tagid, String::GetVoid());
		String prepStr = ToUtf8(prepWStr);
		if (prepStr == "Götz") {
			prepStr.Insert(0, "Gotz!=>");
		}
		fld.SetData(prepStr);
//		fld.WhenAction = THISBACK1(FieldChanged, &fld);	
		fld.ClearModify(); // The "SetData" command sets the Modified bit, so clear it so we can trap user changes
		String dt(fld.GetData());
		fld.Tip((const char *)dt); // Set up tip for long values to be displayed past end of field
	}

	//============================================================================================================================
	void UpdateTagsInDb() {
//		if (!db.IsOpen()) {
//			if (!dbHandler) {
//				Exclamation("No Db Handler");
//				return;
//			}
//			dbHandler->
//			dbHandler.Connect(db); // SLOW: Should be able to clone current connection
//			
//			Sql lst(db);
//			Sql update(db);
//			
//			SqlId SONGS(dbHandler.songsTableName);	
//			// Update?
//		}
	}
	
	//============================================================================================================================
	void GetPrevFileForTagging() {
		if (!selectedList) return;
		if (!selectedList->GetCount() || selectedList->GetCount() == 1) return;
		if (listIndex == -1) return;
		
		listIndex--;
		if (listIndex < 0) {
			listIndex = selectedList->GetCount() - 1;
		}

		// Based on the new index into the grid (playlist), fetch row into a vector and populate the screen		    
		FetchFromGridAndPopulate();
	}

	//============================================================================================================================
	void GetNextFileForTagging() {
		if (!selectedList) return;
		if (!selectedList->GetCount() || selectedList->GetCount() == 1) return;
		if (listIndex == -1) return;
		
		listIndex++;
		if (listIndex > selectedList->GetCount() - 1) {
			listIndex = 0;
		}

		// Based on the new index into the grid (playlist), fetch row into a vector and populate the screen		    
		FetchFromGridAndPopulate();
	}
	
	//============================================================================================================================
	//  Add tag to Vector if we find a matching in column of current list.  It may be a stripped down list.
	//============================================================================================================================
	void AddTagIfPresent(TagVector &tags, PowerArray *selectedList, Id &id, const int tagid, int indexno) {
		if (selectedList->FindColumnWithId(id) != -1) {
			tags.Add(tagid, selectedList->Get(indexno, id)); 
		}
	}

	//============================================================================================================================
	//  Convert the columns in the array row into a vector.
	//============================================================================================================================
	void ConvertRowToTagVector(TagVector &tags, PowerArray *selectedList, int indexno) {
		// C++ issue:  An object just be created locally or the vector won't add it
		AddTagIfPresent(tags, selectedList, IDTitle, TAG_TITLE, indexno);
		AddTagIfPresent(tags, selectedList, IDArtistName, TAG_ARTISTNAME, indexno);
		AddTagIfPresent(tags, selectedList, IDAlbumName, TAG_ALBUMNAME, indexno);
		AddTagIfPresent(tags, selectedList, IDComposerName, TAG_COMPOSERNAME, indexno);
		AddTagIfPresent(tags, selectedList, IDBandName, TAG_BANDNAME, indexno);
		AddTagIfPresent(tags, selectedList, IDConductorName, TAG_CONDUCTORNAME, indexno);
		AddTagIfPresent(tags, selectedList, IDGenreName, TAG_GENRENAME, indexno);
		AddTagIfPresent(tags, selectedList, IDLyrics, TAG_LYRICS, indexno);
		AddTagIfPresent(tags, selectedList, IDReleaseYear, TAG_RELEASEYEAR, indexno);
		AddTagIfPresent(tags, selectedList, IDFilePath, TAG_FILEPATH, indexno);
	}
	
	//============================================================================================================================
	void FetchFromGridAndPopulate() {
		Mutex::Lock __(mutex);
		TagVector dbTags;
		ConvertRowToTagVector(dbTags, selectedList, listIndex);
		
		// Need physical file path from db to fetch file tags
		String lfilePath = dbTags.Get(TAG_FILEPATH);
		TagVector fileTags;
		TagHandler tagHandler; // Create a new instance
		int rtncode = tagHandler.ExtractTags(lfilePath);
		Populate(tagHandler.extractedTags, dbTags, tagHandler.extractedUnrecognizedFrames);
		fileTagDump.SetData(tagHandler.tagDump);
	}
	
	//============================================================================================================================
	void Populate(TagVector &fileTags, TagVector &dbTags, FrameVector &fileFrames) {
		SetupField(fileTitle, fileTags, TAG_TITLE);
		SetupField(fileArtistName, fileTags, TAG_ARTISTNAME);
		SetupField(fileAlbumName, fileTags, TAG_ALBUMNAME);
		SetupField(fileComposerName, fileTags, TAG_COMPOSERNAME);
		SetupField(fileBandName, fileTags, TAG_ARTISTNAME);
		SetupField(fileConductorName, fileTags, TAG_CONDUCTORNAME);
		SetupField(fileGenreName, fileTags, TAG_GENRENAME);
		SetupField(fileReleaseYear, fileTags, TAG_RELEASEYEAR);
		SetupField(fileLyrics, fileTags, TAG_LYRICS);
		
		SetupField(dbFilePath, dbTags, TAG_FILEPATH);
		SetupField(dbTitle, dbTags, TAG_TITLE);
		SetupField(dbArtistName, dbTags, TAG_ARTISTNAME);
		SetupField(dbAlbumName, dbTags, TAG_ALBUMNAME);
		SetupField(dbComposerName, dbTags, TAG_COMPOSERNAME);
		SetupField(dbBandName, dbTags, TAG_ARTISTNAME);
		SetupField(dbConductorName, dbTags, TAG_CONDUCTORNAME);
		SetupField(dbGenreName, dbTags, TAG_GENRENAME);
		SetupField(dbReleaseYear, dbTags, TAG_RELEASEYEAR);
		SetupField(dbLyrics, dbTags, TAG_LYRICS);

		lstTags.Clear();
		
		if (!lstTags.GetColumnCount()) {
			lstTags.Clear();
			lstTags.Proportional();
			lstTags.AddColumn("Tag").Fixed(60);
			lstTags.AddColumn("Value");
		}
		
		for(int k = 0; k < fileTags.GetCount(); k++) {
			lstTags.Add();
			int tagno = fileTags.GetKey(k);
			if (tagno == TAG_PAIR) {
				// Process the pair vector from a TXXX string pair.  Extract the tag descr and value
				ValueArray va = fileTags[k];
				lstTags.Set(0, va[0]); // Check tagHandler.ExtractTags for Elements::TXXX; we build an array to pass the values
				lstTags.Set(1, va[1]);
			} else {
				lstTags.Set(0, tagHandler.GetTagName(tagno));
				lstTags.Set(1, fileTags[k]);
			}
		}
		
		// Now append the unrecognized frames, which don't have an internal id assigned
		for(int k = 0; k < fileFrames.GetCount(); k++) {
			lstTags.Add();
			lstTags.Set(0, fileFrames.GetKey(k));
			lstTags.Set(1, fileFrames[k]);
		}
		
	}

};

#endif
